(*
file: lexer.mll
Arnaud Aujon   AUJA31018906
*)

{
  open Grammar          (* the parser file is "grammar.mly" *)

  exception Eof ;;
  let lineno = ref 1 ;;      (* count line number *)
  let count_nl s = 
    let rec count_int s size = 
      begin match size with
        -1 ->  ()
      | x ->   if (String.get s x == '\n')
          then begin
            incr lineno ;
            count_int s (x-1)
          end else
            count_int s (x-1) 
      end
    in (count_int s (String.length(s)-1))
  ;;
}

let digit = ['0'-'9']
let lowercase = ['a'-'z']
let uppercase = ['A'-'Z']

let skip_nl = [' ' '\t' '\n']*


rule token = parse
      (*** Separateurs ***)
  | [' ' '\t']                   { token lexbuf }
  | '\n'skip_nl                  { count_nl (Lexing.lexeme lexbuf) ; NEWLINE }
  | ','                          { COMMA }
      (*** Types ***)
  | digit+ as num                { INTEGER (int_of_string num) }
  | '-'digit+ as num             { INTEGER (int_of_string num) }

  | digit+'.'digit+ as num       { FLOAT (float_of_string num) }
  | '-'digit+'.'digit+ as num    { FLOAT (float_of_string num) }

  | "true"|"false"               { BOOLEAN (bool_of_string(Lexing.lexeme lexbuf)) }
  | '"'                          { parse_string "" lexbuf }

      (*** Operateurs et comparateurs arithmétiques ***)
  | ['+' '-' '*' '/' '<' '>']    { OPERATOR_BIN (Lexing.lexeme lexbuf)}
  | "=="|"!="|"<="|">=" as v     { OPERATOR_BIN v }
  | "**"|"&&"|"||"   as v        { OPERATOR_BIN v }
  | "not"|"!"   as v             { OPERATOR_UNARY v }
  | '='                          { EQUAL }
      (*** Brackets ***)
  | '('                          { LPAREN }
  | ')'                          { RPAREN }
  | '['                          { LBRACKET }
  | ']'                          { RBRACKET }
      (*** Structures de controles ***)
  | "if"                         { IF }
  | skip_nl "elsif" as v         { count_nl v ; ELSIF }
  | skip_nl "else" as v          { count_nl v ; ELSE }
  | "for"                        { FOR }
  | "while"                      { WHILE }
  | skip_nl "end"  as v          { count_nl v ; END }
  | "in"                         { IN }
  | "do"                         { DO } 
  | '.'                          { DOT }
      (*** Définition de fonctions ***)
  | "def"                        { DEF }
  | skip_nl "return" as v        { count_nl v ; RETURN }
      (*** Fonctions d'affichage ***)
  | "print" | "puts"             { PRINT }
      (*** Variable ***)
  | ('_'|lowercase+)(digit|lowercase|uppercase|'_')*|"$*"
                                 { VAR (Lexing.lexeme lexbuf) }
  | uppercase+                   { VAR (Lexing.lexeme lexbuf) }
        (*** Comments ***) 
  | "=begin"                     { multi_line_comment 1 lexbuf }
  | "=end"                       { failwith "too many end comment" }
  | '#'                          { one_line_comment lexbuf }
      (*** End of File et mots non reconnus ***)
  | eof                          { raise Eof }
  | _                            { failwith 
                            ("Bad token on line "^string_of_int !lineno) }

(* machine to parse a string *)
and parse_string str = parse
   '"'                          { STRING(str) }
  | _ as c                      { parse_string (str^(String.make 1 c)) lexbuf }
  
(* machine to parse one-line comments *)
and one_line_comment = parse
   '\n'                         { incr lineno ; token lexbuf }
  | _                           { one_line_comment lexbuf }

(* machine to parse multi-line nested comments *)
and multi_line_comment deep = parse
   "=end"    { if ( deep == 1 ) then token lexbuf else multi_line_comment
                                                           (deep-1) lexbuf }
  | "=begin"                   { multi_line_comment (deep+1) lexbuf }
  | '\n'                       { incr lineno ; multi_line_comment deep lexbuf }
  | eof                        { failwith ("too many begin comment") }
  | _                          { multi_line_comment deep lexbuf }